#include <iostream>
#include <string>
#include <memory>

class Fruit
{
public:
    virtual void name() = 0;
};

class Apple : public Fruit
{
public:
    void name() override
    {
        std::cout << "我是一个苹果！" << std::endl;
    }
};

class Banana : public Fruit
{
public:
    void name() override
    {
        std::cout << "我是一个香蕉！" << std::endl;
    }
};

// 简单工厂模式：通过参数控制可以生产任何产品
// 优点：点单粗暴，直观易懂。使用一个工厂生产统一结构下的任意产品
// 缺点：
//       1.所有产品生产在一起，产品太多会导致代码量庞大
//       2.开闭原则遵循（开放扩展，关闭修改）的不太好，需要新增产品就必须修工厂方法

// class FruitFactory
// {
// public:
//     static std::shared_ptr<Fruit> create(const std::string &name)
//     {
//         if (name == "苹果")
//         {
//             return std::make_shared<Apple>();
//         }
//         else
//         {
//             return std::make_shared<Banana>();
//         }
//     }
// };

// 工厂方法模式：定义一个创建对象接口，但是由其子类来确定创建那种对象，使用多个工厂分别生产指定的产品
// 优点：
//       1.减轻了工厂类的负担，将某种产品的生产交给指定的工厂来进行
//       2.开闭原则遵循较好，添加新产品只需要新增生产对应产品的工厂，不需要修改原先的工厂类
// 缺点：对于某种可以形成一组产品族的情况处理起来较为复杂，需要创建大量工厂类
//

// class FruitFactory
// {
// public:
//     virtual std::shared_ptr<Fruit> create() = 0;
// };

// class AppleFactory : public FruitFactory
// {
// public:
//     std::shared_ptr<Fruit> create() override
//     {
//         return std::make_shared<Apple>();
//     }
// };

// class BananaFactory : public FruitFactory
// {
// public:
//     std::shared_ptr<Fruit> create() override
//     {
//         return std::make_shared<Banana>();
//     }
// };

// 抽象工厂模式：抽象出一个万能工厂来创建具体种类的子类的工厂，然后用这些不同的子类工厂去生产对应的产品
class Animal
{
public:
    virtual void name() = 0;
};

class Lamp : public Animal
{
public:
    void name() override
    {
        std::cout << "我是一只山羊！" << std::endl;
    }
};

class Dog : public Animal
{
public:
    void name() override
    {
        std::cout << "我是一只小狗！" << std::endl;
    }
};

class Factory
{
public:
    virtual std::shared_ptr<Fruit> getFruit(const std::string &name) = 0;
    virtual std::shared_ptr<Animal> getAnimal(const std::string &name) = 0;
};

class FruitFactory : public Factory
{
public:
    std::shared_ptr<Fruit> getFruit(const std::string &name)
    {
        if(name=="苹果")
        {
            return std::make_shared<Apple>();
        }
        else if(name=="香蕉")
        {
            return std::make_shared<Banana>();
        }
        else
        {
            return std::shared_ptr<Fruit>();
        }
    }

    std::shared_ptr<Animal> getAnimal(const std::string &name)
    {
        return std::shared_ptr<Animal>();
    }
};

class AnimalFactory : public Factory
{
public:
    std::shared_ptr<Fruit> getFruit(const std::string &name)
    {
        return std::shared_ptr<Fruit>();
    }

    std::shared_ptr<Animal> getAnimal(const std::string &name)
    {
        if(name=="山羊")
        {
            return std::make_shared<Lamp>();
        }
        else if(name=="小狗")
        {
            return std::make_shared<Dog>();
        }
        else
        {
            return std::shared_ptr<Animal>();
        }
    }
};

class FactoryProducer
{
public:
    static std::shared_ptr<Factory> create(const std::string &name)
    {
        if(name=="水果")
        {
            return std::make_shared<FruitFactory>();
        }
        else if(name=="动物")
        {
            return std::make_shared<AnimalFactory>();
        }
        else
        {
            return std::shared_ptr<Factory>();
        }
    }
};

int main()
{
    std::shared_ptr<Factory> ff=FactoryProducer::create("水果");
    std::shared_ptr<Fruit> fruit=ff->getFruit("苹果");
    fruit->name();
    fruit=ff->getFruit("香蕉");
    fruit->name();

    ff.reset(new AnimalFactory());
    std::shared_ptr<Animal> animal=ff->getAnimal("山羊");
    animal->name();
    animal=ff->getAnimal("小狗");
    animal->name();

    // std::shared_ptr<FruitFactory> ff(new AppleFactory());
    // std::shared_ptr<Fruit> fruit = ff->create();
    // fruit->name();
    // ff.reset(new BananaFactory());
    // fruit = ff->create();
    // fruit->name();

    // std::shared_ptr<Fruit> fruit = FruitFactory::create("苹果");
    // fruit->name();
    // fruit = FruitFactory::create("香蕉");
    // fruit->name();

    return 0;
}
